/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Library General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301,  USA
 */

/*
 * @version bsp.comoro.Package v 1.0.0
 * @author Dr.NP <np@bsgroup.org>
 * @package BS.Play
 *
 * Comoro actionscript3 packager
 */

package bsp.comoro
{
	import flash.utils.ByteArray;
	
	public class Package
	{
		/* Constuctor */
		public function Package() : void
		{
			return;
		}
		
		/* Pack data into a single string */
		public function pack(cmd_id : uint, params : Array) : ByteArray
		{
			var r : ByteArray = new ByteArray();
			var d : ByteArray = new ByteArray();
			var plen : uint = 0;
			
			for (var i : uint = 0; i < params.length; i ++)
			{
				var tmp_l : uint;
				var tmp_t : uint;
				
				switch (typeof params[i])
				{
					case 'boolean' : 
						var tmp_b : Boolean = params[i];
						tmp_t = 0x1;
						d.writeByte(tmp_t);
						
						if (tmp_b)
						{
							// True
							d.writeByte(1);
						}
						
						else
						{
							// False
							d.writeByte(0);
						}
						
						plen ++;
						
						break;
					
					case 'number' : 
						var tmp_n : Number = params[i];
						
						if (tmp_n != Math.floor(tmp_n))
						{
							// Float
							tmp_t = 0x20;
							tmp_l = 4;
							
							// I don't like IEEE 754 now~~~
							/*
							d.writeByte(tmp_t);
							d.writeFloat(tmp_n);
							*/
						}
						
						else
						{
							if (tmp_n < 0x10000)
							{
								// Short
								tmp_t = 0x2
								tmp_l = 2;
								
								d.writeByte(tmp_t);
								d.writeShort(tmp_n);
							}
							
							else if (tmp_n < 0x100000000)
							{
								// Single
								tmp_t = 0x4;
								tmp_l = 4;
								
								d.writeByte(tmp_t);
								d.writeUnsignedInt(tmp_n);
							}
							
							else if (tmp_n < 0x10000000000000000)
							{
								// Long
								tmp_t = 0x8
								tmp_l = 8;
								
								d.writeByte(tmp_t);
								d.writeUnsignedInt(Math.floor(tmp_n / 0x100000000));
								d.writeUnsignedInt(tmp_n % 0x100000000);
							}
							
							else
							{
								// So huge?
								tmp_t = 0x10;
								tmp_l = 16;
								
								// IGNORED
							}
						}
						
						plen ++;
						
						break;
					
					case 'string' : 
						var tmp_s : String = params[i];
						var tmp_s_d : ByteArray = new ByteArray();
						tmp_s_d.writeUTFBytes(tmp_s);
						
						tmp_t = 0xff;
						tmp_l = tmp_s_d.length;
						
						
						d.writeByte(tmp_t);
						d.writeUnsignedInt(tmp_l);
						d.writeBytes(tmp_s_d);
						
						plen ++;
						
						break;
					
					case 'object' : 
					case 'function' : 
					case 'xml' : 
					default : 
						// Unsupported type
						break;
				}
			}
			
			r.writeUnsignedInt(cmd_id);
			r.writeUnsignedInt(plen);
			r.writeBytes(d);
			
			return r;
		}
		
		/* Unpack data from sigle string */
		public function unpack(d : ByteArray) : Array
		{
			var r : Array = new Array();
			var params : Array = new Array();
			d.position = 0;
			if (d.length < 8)
			{
				// Data length error
				return new Array();
			}
			
			var cmd_id : uint = d.readUnsignedInt();
			var plen : uint = d.readUnsignedInt();
			var tmp_t : uint;
			var tmp_l : uint;
			
			r[0] = cmd_id;
			
			// Write params
			
			for(var i : uint = 0; i < plen; i ++)
			{
				tmp_t = d.readUnsignedByte();
				
				switch (tmp_t)
				{
					case 0x1 : 
						// Boolean
						tmp_l = 1;
						if (0 == d.readByte())
						{
							params[i] = false;
						}
						
						else
						{
							params[i] = true;
						}
						
						break;
					
					case 0x2 : 
						// Short
						tmp_l = 2;
						params[i] = d.readUnsignedShort();
						
						break;
					
					case 0x4 : 
						// Unsigned single
						tmp_l = 4;
						params[i] = d.readUnsignedInt();
						
						break;
					
					case 0x8 : 
						// Unsigned Long
						tmp_l = 8;
						params[i] = d.readUnsignedInt() + d.readUnsignedInt() * 0x100000000
						
						break;
					
					case 0x10 : 
						// Huge value? Unsupported now.
						tmp_l = 16;
						
						break;
					
					case 0x20 : 
						// Float
						tmp_l = 4;
						params[i] = d.readFloat();
						
						break;
					
					case 0xff : 
						// String
						tmp_l = d.readUnsignedInt();
						params[i] = d.readUTFBytes((tmp_l > d.bytesAvailable) ? d.bytesAvailable : tmp_l);
						
						break;
					
					default : 
						// UNKNOWN TYPE
						break;
				}
			}
			
			r[1] = params;
			return r;
		}
	}
}